// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package auth.plugins.flutter.io.fist_local_auth;

import android.app.Activity;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.annotation.RequiresApi;
import androidx.fragment.app.FragmentActivity;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.crypto.Cipher;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;

import static auth.plugins.flutter.io.fist_local_auth.CipherHelper.DEFAULT_KEY_NAME;
import static auth.plugins.flutter.io.fist_local_auth.CipherHelper.HAS_FINGER_KEY;

/**
 * FistLocalAuthPlugin
 */
@SuppressWarnings("deprecation")
@RequiresApi(api = Build.VERSION_CODES.M)
public class FistLocalAuthPlugin implements MethodCallHandler {
    private final Registrar registrar;
    private final AtomicBoolean authInProgress = new AtomicBoolean(false);

    /**
     * Plugin registration.
     */
    public static void registerWith(Registrar registrar) {
        final MethodChannel channel =
                new MethodChannel(registrar.messenger(), "auth.plugins.flutter.io/fist_local_auth");
        channel.setMethodCallHandler(new FistLocalAuthPlugin(registrar));
    }

    private FistLocalAuthPlugin(Registrar registrar) {
        this.registrar = registrar;
    }

    @Override
    public void onMethodCall(MethodCall call, final Result result) {
        Activity activity = registrar.activity();
        if (activity == null || activity.isFinishing()) {
            authInProgress.compareAndSet(true, false);
            result.error("no_activity", "local_auth plugin requires a foreground activity", null);
            return;
        }
        if (call.method.equals("clearKeys")) {
            SharedPreferences sharedPreferences = activity.getSharedPreferences(DEFAULT_KEY_NAME, 0);
            sharedPreferences.edit().putString(HAS_FINGER_KEY, "").apply();
            result.success(true);
            return;
        }
        if (call.method.equals("touchIdInfoDidChange")) {
            try {
                SharedPreferences sharedPreferences = activity.getSharedPreferences(DEFAULT_KEY_NAME, 0);
                String temp = sharedPreferences.getString(HAS_FINGER_KEY, "");
                if (temp.isEmpty()) {
                    result.success(false);
                    return;
                }
                Cipher mCipher = CipherHelper.getInstance().createCipher();
                if (CipherHelper.getInstance().initCipher(mCipher)) {
                    result.success(true);
                    return;
                } else {
                    result.success(false);
                    return;
                }
            } catch (Exception e) {
                result.success(false);
                e.printStackTrace();
            }
        }
        if (call.method.equals("authenticateWithBiometrics")) {
            if (!authInProgress.compareAndSet(false, true)) {
                // Apps should not invoke another authentication request while one is in progress,
                // so we classify this as an error condition. If we ever find a legitimate use case for
                // this, we can try to cancel the ongoing auth and start a new one but for now, not worth
                // the complexity.
                result.error("auth_in_progress", "Authentication in progress", null);
                return;
            }

            if (!(activity instanceof FragmentActivity)) {
                result.error(
                        "no_fragment_activity",
                        "local_auth plugin requires activity to be a FragmentActivity.",
                        null);
                return;
            }
            AuthenticationHelper authenticationHelper =
                    new AuthenticationHelper(
                            (FragmentActivity) activity,
                            call,
                            new AuthenticationHelper.AuthCompletionHandler() {
                                @Override
                                public void onChanged(String code, String error) {
                                    if (authInProgress.compareAndSet(true, false)) {
                                        result.error(code + "", error, null);
                                    }
                                }

                                @Override
                                public void onSuccess() {
                                    if (authInProgress.compareAndSet(true, false)) {
                                        result.success(true);
                                    }
                                }

                                @Override
                                public void onFailure(int code, String errString) {
                                    if (authInProgress.compareAndSet(true, false)) {
                                        result.error(code + "", errString, null);
                                    }
                                }

                                @Override
                                public void onError(String code, String error) {
                                    if (authInProgress.compareAndSet(true, false)) {
                                        result.error(code, error, null);
                                    }
                                }
                            });
            authenticationHelper.authenticate();
        } else if (call.method.equals("getAvailableBiometrics")) {
            try {
                ArrayList<String> biometrics = new ArrayList<String>();
                PackageManager packageManager = activity.getPackageManager();
                if (Build.VERSION.SDK_INT >= 23) {
                    if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
                        biometrics.add("fingerprint");
                    }
                }
                if (Build.VERSION.SDK_INT >= 29) {
                    if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
                        biometrics.add("face");
                    }
                    if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
                        biometrics.add("iris");
                    }
                }
                result.success(biometrics);
            } catch (Exception e) {
                result.error("no_biometrics_available", e.getMessage(), null);
            }
        } else {
            result.notImplemented();
        }
    }
}
